{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center> Introduction To Gevent</center>\n",
    "\n",
    "\n",
    "<center><img align=\"center\" src=\"imgs/gevent_cover.png\"></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>简介</center>\n",
    "\n",
    "- 基于 libev 的并发库\n",
    "- Greenlet, 它是以C扩展模块形式接入Python的轻量级协程\n",
    "- Greenlet全部运行在主程序操作系统进程的内部，但它们被协作式地调度。在任何时刻，只有一个协程在运行\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>简介</center>\n",
    "\n",
    "- 并发的核心思想在于，大的任务可以分解成一系列的子任务，后者可以被调度成同时执行或异步执行\n",
    "- 两个子任务之间的 切换也就是上下文切换\n",
    "- 在gevent里面，上下文切换是通过yielding来完成的"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Running in foo\n",
      "Explicit context to bar\n",
      "Explicit context switch to foo again\n",
      "Implicit context switch back to bar\n",
      "hehe\n"
     ]
    }
   ],
   "source": [
    "import gevent\n",
    "\n",
    "def foo():\n",
    "    print('Running in foo')\n",
    "    gevent.sleep(0)\n",
    "    print('Explicit context switch to foo again')\n",
    "\n",
    "def bar():\n",
    "    print('Explicit context to bar')\n",
    "    gevent.sleep(0)\n",
    "    print('Implicit context switch back to bar')\n",
    "\n",
    "gevent.joinall([\n",
    "    gevent.spawn(foo),\n",
    "    gevent.spawn(bar),\n",
    "])\n",
    "print('hehe')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "<center><img align=\"center\" src=\"imgs/gevent_yield.gif\"></center>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>协作式调度</center>\n",
    "\n",
    "- 网络IO中 gevent 负责协作式调度 greenlet\n",
    "- 不用关心底层细节就能实现网络并发"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Synchronous:\n",
      "Task 1 done\n",
      "Task 2 done\n",
      "Task 3 done\n",
      "Task 4 done\n",
      "Task 5 done\n",
      "Task 6 done\n",
      "Task 7 done\n",
      "Task 8 done\n",
      "Task 9 done\n",
      "Asynchronous:\n",
      "Task 3 done\n",
      "Task 5 done\n",
      "Task 1 done\n",
      "Task 4 done\n",
      "Task 6 done\n",
      "Task 8 done\n",
      "Task 9 done\n",
      "Task 0 done\n",
      "Task 2 done\n",
      "Task 7 done\n"
     ]
    }
   ],
   "source": [
    "import gevent\n",
    "import random\n",
    "\n",
    "def task(pid):\n",
    "    gevent.sleep(random.randint(0,2)*0.001)\n",
    "    print('Task %s done' % pid)\n",
    "\n",
    "def synchronous():\n",
    "    for i in range(1,10):\n",
    "        task(i)\n",
    "\n",
    "def asynchronous():\n",
    "    threads = [gevent.spawn(task, i) for i in range(10)]\n",
    "    gevent.joinall(threads)\n",
    "\n",
    "print('Synchronous:')\n",
    "synchronous()\n",
    "\n",
    "print('Asynchronous:')\n",
    "asynchronous()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Synchronous:\n",
      "0.21242594718933105\n",
      "Asynchronous:\n",
      "0.06549978256225586\n"
     ]
    }
   ],
   "source": [
    "import gevent.monkey\n",
    "gevent.monkey.patch_socket()\n",
    "\n",
    "import gevent\n",
    "import requests\n",
    "import json, time\n",
    "\n",
    "def fetch(pid):\n",
    "    response = requests.get('http://www.baidu.com')\n",
    "\n",
    "def synchronous():\n",
    "    for i in range(1,10):\n",
    "        fetch(i)\n",
    "\n",
    "def asynchronous():\n",
    "    threads = []\n",
    "    for i in range(1,10):\n",
    "        threads.append(gevent.spawn(fetch, i))\n",
    "    gevent.joinall(threads)\n",
    "beg = time.time()\n",
    "print('Synchronous:')\n",
    "synchronous()\n",
    "print(time.time()-beg)\n",
    "\n",
    "beg = time.time()\n",
    "print('Asynchronous:')\n",
    "asynchronous()\n",
    "print(time.time()-beg)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>创建 Greenlets</center>\n",
    "\n",
    "- 使用 Greenlet.spawn\n",
    "- 子类化 Greenlet类，重写 _run 方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hello\n",
      "I live!\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[<Greenlet \"Greenlet-6\" at 0x10584f048: _run>,\n",
       " <Greenlet \"Greenlet-7\" at 0x104c5c148: _run>,\n",
       " <Greenlet \"Greenlet-8\" at 0x104c5cd48: _run>]"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import gevent\n",
    "from gevent import Greenlet\n",
    "\n",
    "def foo(message, n):\n",
    "    gevent.sleep(n)\n",
    "    print(message)\n",
    "\n",
    "# Initialize a new Greenlet instance running the named function\n",
    "# foo\n",
    "thread1 = Greenlet.spawn(foo, \"Hello\", 1)\n",
    "\n",
    "# Wrapper for creating and running a new Greenlet from the named\n",
    "# function foo, with the passed arguments\n",
    "thread2 = gevent.spawn(foo, \"I live!\", 2)\n",
    "\n",
    "# Lambda expressions\n",
    "thread3 = gevent.spawn(lambda x: (x+1), 2)\n",
    "\n",
    "threads = [thread1, thread2, thread3]\n",
    "\n",
    "# Block until all threads complete.\n",
    "gevent.joinall(threads)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hi there!\n"
     ]
    }
   ],
   "source": [
    "import gevent\n",
    "from gevent import Greenlet\n",
    "\n",
    "class MyGreenlet(Greenlet):\n",
    "\n",
    "    def __init__(self, message, n):\n",
    "        Greenlet.__init__(self)\n",
    "        self.message = message\n",
    "        self.n = n\n",
    "\n",
    "    def _run(self):\n",
    "        print(self.message)\n",
    "        gevent.sleep(self.n)\n",
    "\n",
    "g = MyGreenlet(\"Hi there!\", 3)\n",
    "g.start()\n",
    "g.join()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>Greenlet 状态</center>\n",
    "\n",
    "- started -- Boolean, 指示此Greenlet是否已经启动\n",
    "- ready() -- Boolean, 指示此Greenlet是否已经停止\n",
    "- successful() -- Boolean, 指示此Greenlet是否已经停止而且没抛异常\n",
    "- value -- 任意值, 此Greenlet代码返回的值\n",
    "- exception -- 异常, 此Greenlet内抛出的未捕获异常"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "import gevent\n",
    "\n",
    "def win():\n",
    "    return 'You win!'\n",
    "\n",
    "def fail():\n",
    "    raise Exception('You fail at failing.')\n",
    "\n",
    "winner = gevent.spawn(win)\n",
    "loser = gevent.spawn(fail)\n",
    "\n",
    "print(winner.started) # True\n",
    "print(loser.started)  # True\n",
    "\n",
    "# Exceptions raised in the Greenlet, stay inside the Greenlet.\n",
    "try:\n",
    "    gevent.joinall([winner, loser])\n",
    "except Exception as e:\n",
    "    print('This will never be reached')\n",
    "\n",
    "print(winner.value) # 'You win!'\n",
    "print(loser.value)  # None\n",
    "\n",
    "print(winner.ready()) # True\n",
    "print(loser.ready())  # True\n",
    "\n",
    "print(winner.successful()) # True\n",
    "print(loser.successful())  # False\n",
    "\n",
    "# The exception raised in fail, will not propogate outside the\n",
    "# greenlet. A stack trace will be printed to stdout but it\n",
    "# will not unwind the stack of the parent.\n",
    "\n",
    "print(loser.exception)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>猴子补丁(Monkey Patching)</center>\n",
    "\n",
    "- 黑魔法，运行时属性替换。有用的邪恶(useful evil)\n",
    "- gevent能够 修改标准库里面大部分的阻塞式系统调用，包括socket、ssl、threading和 select等模块，而变为协作式运行\n",
    "- from gevent import monkey; monkey.patch_socket()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "outputs": [],
   "source": [
    "import socket\n",
    "print(socket.socket)\n",
    "\n",
    "print(\"After monkey patch\")\n",
    "from gevent import monkey\n",
    "monkey.patch_socket()\n",
    "print(socket.socket)\n",
    "\n",
    "import select\n",
    "print(select.select)\n",
    "monkey.patch_select()\n",
    "print(\"After monkey patch\")\n",
    "print(select.select)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>数据结构</center>\n",
    "\n",
    "- event, 在Greenlet之间异步通信的形式\n",
    "- Queue, 队列是一个排序的数据集合，它有常见的put / get操作， 但是它是以在Greenlet之间可以安全操作的方式来实现的\n",
    "- group, 是一个运行中greenlet的集合，集合中的greenlet像一个组一样 会被共同管理和调度\n",
    "- pool, 是一个为处理数量变化并且需要限制并发的greenlet而设计的结构。 在需要并行地做很多受限于网络和IO的任务时常常需要用到它\n",
    "- BoundedSemaphore, 信号量, 允许greenlet相互合作，限制并发访问或运行的低层次的同步原语， 信号量有两个方法，acquire和release\n",
    "- greenlet，允许你指定局部于greenlet上下文的数据"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    }
   },
   "source": [
    "# <center>其他模块</center>\n",
    "\n",
    "- StreamServer\n",
    "- WSGI Servers\n",
    "- websocket"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# <center>参考</center>\n",
    "\n",
    "- [Gevent 程序员指南](http://ningning.today/gevent-tutorial-cn/)"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "幻灯片",
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
